/* Copyright (C)
 * 2017 - Xianlee.Wu  xianleewu@163.com
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 */

#include "stdio.h"
#include "stdint.h"
#include "stdlib.h"
#include "string.h"
#include "fcntl.h"
#include "netdb.h"
#include "unistd.h"
#include "pthread.h"
#include "lewei_ctrl.h"


static int wait_on_socket(int sockfd, int for_recv, long timeout_ms)
{
    struct timeval tv;
    fd_set infd, outfd, errfd;
    int res;

    tv.tv_sec = timeout_ms / 1000;
    tv.tv_usec= (timeout_ms % 1000) * 1000;

    FD_ZERO(&infd);
    FD_ZERO(&outfd);
    FD_ZERO(&errfd);

    FD_SET(sockfd, &errfd); /* always check for error */

    if(for_recv) {
        FD_SET(sockfd, &infd);
    }
    else {
        FD_SET(sockfd, &outfd);
    }

    /* select() returns the number of signalled sockets or -1 */
    res = select((int)sockfd + 1, &infd, &outfd, &errfd, &tv);

    return res;
}

static int create_tcp_client(const char* host, int port)
{
    int socket_fd = -1;
    struct hostent *he = NULL;
    struct sockaddr_in their_addr;

    if(!host || port < 0) {
        printf("please special your server info!\n");
        return -1;
    }

    if((he=gethostbyname(host)) == NULL) {
        printf("cant get host info from input!\n");
        return -1;
    }

    if((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        printf("failed to create socket!\n");
        return -1;
    }

    their_addr.sin_family = AF_INET;
    their_addr.sin_port = htons(port);
    their_addr.sin_addr = *((struct in_addr*)he->h_addr);
    bzero(&(their_addr.sin_zero), 8);

    if((connect(socket_fd, (struct sockaddr*)&their_addr, sizeof(struct sockaddr))) == -1) {
        printf("failed to connect to server!\n");
        return -1;
    }

    return socket_fd;
}

static void close_tcp_client(int socket_fd)
{
    shutdown(socket_fd, SHUT_RDWR);
    close(socket_fd);
}

int lewei_ctrl_init(lewei_client_t* client, int gateway_num, const char* userkey)
{
    if(!client) {
        printf("please input a valid client!\n");
        return -1;
    }

    memset(client, 0, sizeof(lewei_client_t));

    pthread_mutex_init(&(client->mutex), NULL);

    if((client->socket_fd = create_tcp_client(LEWEI_SERVER_ADDR, LEWEI_SERVER_PORT)) < 0) {
        return -1;
    }

    snprintf(client->userkey, LEWEI_USERKEY_MAX_SIZE, "%s", userkey);
    client->gateway_num = gateway_num;
    client->is_connected = 1;
    client->is_running = 1;

    return 0;
}

int lewei_ctrl_destroy(lewei_client_t* client)
{
    if(!client) {
        printf("please input a valid client!\n");
        return -1;
    }

    if(client->socket_fd) {
        close_tcp_client(client->socket_fd);
        client->socket_fd = -1;
    }

    return 0;
}

void lewei_toggle_on()
{
    printf("\n************************************************\n");
    printf("* we got ON cmd from server, now turn on lights *");
    printf("\n************************************************\n");
}

void lewei_toggle_off()
{
    printf("\n************************************************\n");
    printf("* we got OFF cmd from server, now turn on lights *");
    printf("\n************************************************\n");
}

void recv_calback(lewei_client_t* client, char* data, int len)
{
    char* func_start = NULL;
    char* p1_start = NULL;

    func_start = strstr(data, LEWEI_CMD_TOGGLE);

    if(!client || !func_start || (len < (int)(sizeof(LEWEI_CMD_TOGGLE) + sizeof(LEWEI_CMD_TOGGLE_TRUE)))) {
        printf("got data from server, but it is not what we expected!\n");
        return;
    }

    p1_start = func_start + sizeof(LEWEI_CMD_TOGGLE);

    printf("\np1----->:%s\n", p1_start);

    if(0 == strncmp(p1_start, LEWEI_CMD_TOGGLE_TRUE, strlen(LEWEI_CMD_TOGGLE_TRUE))) {
        lewei_toggle_on();
    } else if(0 == strncmp(p1_start, LEWEI_CMD_TOGGLE_FALSE, strlen(LEWEI_CMD_TOGGLE_FALSE))) {
        lewei_toggle_off();
    } else {
        printf("got toggle cmd from server, but the content is not valid\n");
    }
}

void* lewei_ctrl_recv_cmd(void* arg)
{
    lewei_client_t* client = (lewei_client_t*)arg;
    char recv_buf[MAX_DATA_SIZE] = {0};

    while(client->is_running) {
        if(client->is_connected) {
            int recv_len = -1;

            memset(recv_buf, 0, MAX_DATA_SIZE);
            recv_len = recv(client->socket_fd, recv_buf, MAX_DATA_SIZE, 0);

            if(recv_len < 0) {
                printf("Error:receive data failed!\n");
                client->is_connected = -1;
            } else {
                printf("data from server:%s\n", recv_buf);
                recv_calback(client, recv_buf, recv_len);
            }
        } else {
            sleep(2);
        }
    }

    printf("lewei: receive data thread dead!!!\n");

    pthread_exit(NULL);
}

void* lewei_ctrl_check_connection(void* arg)
{
    lewei_client_t* client = (lewei_client_t*)arg;

    while(client->is_running) {
        if((wait_on_socket(client->socket_fd, 0, CONNECTION_TIMEOUT)) < 0) {
            printf("waiting for socket timed out!\n");
            client->is_connected = -1;
            close_tcp_client(client->socket_fd);
            client->socket_fd = -1;
        }
    }

    printf("lewei: checking connection thread dead!!!\n");

    pthread_exit(NULL);
}

int lewei_ctrl_send_heartbeat(lewei_client_t* client)
{
    char send_buf[MAX_DATA_SIZE] = {0};

    snprintf(send_buf, MAX_DATA_SIZE, LEWEI_REG_FORMAT, client->gateway_num, client->userkey);

    return send(client->socket_fd, send_buf, strlen(send_buf), 0);
}

void* lewei_ctrl_keep_connection(void *arg)
{
    lewei_client_t* client = (lewei_client_t*)arg;

    while(client->is_running) {
        if(!client->is_connected) {
            printf("trying connect to server...!\n");
            if((client->socket_fd = create_tcp_client(LEWEI_SERVER_ADDR, LEWEI_SERVER_PORT)) < 0) {
                printf("failed to reconnect...retry...\n");
            } else {
                client->is_connected = 1;
            }
        } else {
            if(0 > lewei_ctrl_send_heartbeat(client)) {
                client->is_connected = -1;
            }
        }
        sleep(RECONNECT_TIME);
    }

    printf("lewei: keep connection thread dead!!!\n");

    pthread_exit(NULL);
}
